package it.bsoft.gameboy.cpu;

import java.util.HashMap;

public enum OpCode {

	/**
	 * Store Byte Indirect
	 */
	
	/**
	 * Move Byte Immediate: reg<-mem[n]
	 */
	
	LD_B_n(0x06,8,(cpu)->{cpu.loadByteRegToRam(cpu.getBRegister());}),
	LD_C_n(0x0E,8,(cpu)->{cpu.loadByteRegToRam(cpu.getCRegister());}),
	LD_D_n(0x16,8,(cpu)->{cpu.loadByteRegToRam(cpu.getDRegister());}),
	LD_E_n(0x1E,8,(cpu)->{cpu.loadByteRegToRam(cpu.getERegister());}),
	LD_H_n(0x26,8,(cpu)->{cpu.loadByteRegToRam(cpu.getHRegister());}),
	LD_L_n(0x2E,8,(cpu)->{cpu.loadByteRegToRam(cpu.getLRegister());}),
	
	/**
	 * Load Byte Indirect: (dest)<-reg
	 */
	LD_A_HL(0x7E,8,(cpu)->{cpu.loadByteRamToRegIndirect(cpu.getARegister(),cpu.getHLRegister());}),
	LD_B_HL(0x46,8,(cpu)->{cpu.loadByteRamToRegIndirect(cpu.getBRegister(),cpu.getHLRegister());}),
	LD_C_HL(0x4E,8,(cpu)->{cpu.loadByteRamToRegIndirect(cpu.getCRegister(),cpu.getHLRegister());}),
	LD_D_HL(0x56,8,(cpu)->{cpu.loadByteRamToRegIndirect(cpu.getDRegister(),cpu.getHLRegister());}),
	LD_E_HL(0x5E,8,(cpu)->{cpu.loadByteRamToRegIndirect(cpu.getERegister(),cpu.getHLRegister());}),
	LD_H_HL(0x66,8,(cpu)->{cpu.loadByteRamToRegIndirect(cpu.getHRegister(),cpu.getHLRegister());}),
	LD_L_HL(0x6E,8,(cpu)->{cpu.loadByteRamToRegIndirect(cpu.getLRegister(),cpu.getHLRegister());}),
	
	/**
	 * Move Byte reg1->reg2
	 */
	
	LD_A_A(0x7F,4,(cpu)->{cpu.loadByteRegToReg(cpu.getARegister(),cpu.getARegister());}),
	LD_A_B(0x78,4,(cpu)->{cpu.loadByteRegToReg(cpu.getARegister(),cpu.getBRegister());}),
	LD_A_C(0x79,4,(cpu)->{cpu.loadByteRegToReg(cpu.getARegister(),cpu.getCRegister());}),
	LD_A_D(0x7A,4,(cpu)->{cpu.loadByteRegToReg(cpu.getARegister(),cpu.getDRegister());}),
	LD_A_E(0x7B,4,(cpu)->{cpu.loadByteRegToReg(cpu.getARegister(),cpu.getERegister());}),
	LD_A_H(0x7C,4,(cpu)->{cpu.loadByteRegToReg(cpu.getARegister(),cpu.getHRegister());}),
	LD_A_L(0x7D,4,(cpu)->{cpu.loadByteRegToReg(cpu.getARegister(),cpu.getLRegister());}),
	
	LD_B_B(0x40,4,(cpu)->{cpu.loadByteRegToReg(cpu.getBRegister(),cpu.getBRegister());}),
	LD_B_C(0x41,4,(cpu)->{cpu.loadByteRegToReg(cpu.getBRegister(),cpu.getCRegister());}),
	LD_B_D(0x42,4,(cpu)->{cpu.loadByteRegToReg(cpu.getBRegister(),cpu.getDRegister());}),
	LD_B_E(0x43,4,(cpu)->{cpu.loadByteRegToReg(cpu.getBRegister(),cpu.getERegister());}),
	LD_B_H(0x44,4,(cpu)->{cpu.loadByteRegToReg(cpu.getBRegister(),cpu.getHRegister());}),
	LD_B_L(0x45,4,(cpu)->{cpu.loadByteRegToReg(cpu.getBRegister(),cpu.getLRegister());}),
	
	LD_C_B(0x48,4,(cpu)->{cpu.loadByteRegToReg(cpu.getCRegister(),cpu.getBRegister());}),
	LD_C_C(0x49,4,(cpu)->{cpu.loadByteRegToReg(cpu.getCRegister(),cpu.getCRegister());}),
	LD_C_D(0x4A,4,(cpu)->{cpu.loadByteRegToReg(cpu.getCRegister(),cpu.getDRegister());}),
	LD_C_E(0x4B,4,(cpu)->{cpu.loadByteRegToReg(cpu.getCRegister(),cpu.getERegister());}),
	LD_C_H(0x4C,4,(cpu)->{cpu.loadByteRegToReg(cpu.getCRegister(),cpu.getHRegister());}),
	LD_C_L(0x4D,4,(cpu)->{cpu.loadByteRegToReg(cpu.getCRegister(),cpu.getLRegister());}),
	
	LD_D_B(0x50,4,(cpu)->{cpu.loadByteRegToReg(cpu.getDRegister(),cpu.getBRegister());}),
	LD_D_C(0x51,4,(cpu)->{cpu.loadByteRegToReg(cpu.getDRegister(),cpu.getCRegister());}),
	LD_D_D(0x52,4,(cpu)->{cpu.loadByteRegToReg(cpu.getDRegister(),cpu.getDRegister());}),
	LD_D_E(0x53,4,(cpu)->{cpu.loadByteRegToReg(cpu.getDRegister(),cpu.getERegister());}),
	LD_D_H(0x54,4,(cpu)->{cpu.loadByteRegToReg(cpu.getDRegister(),cpu.getHRegister());}),
	LD_D_L(0x55,4,(cpu)->{cpu.loadByteRegToReg(cpu.getDRegister(),cpu.getLRegister());}),
	
	LD_E_B(0x58,4,(cpu)->{cpu.loadByteRegToReg(cpu.getERegister(),cpu.getBRegister());}),
	LD_E_C(0x59,4,(cpu)->{cpu.loadByteRegToReg(cpu.getERegister(),cpu.getCRegister());}),
	LD_E_D(0x5A,4,(cpu)->{cpu.loadByteRegToReg(cpu.getERegister(),cpu.getDRegister());}),
	LD_E_E(0x5B,4,(cpu)->{cpu.loadByteRegToReg(cpu.getERegister(),cpu.getERegister());}),
	LD_E_H(0x5C,4,(cpu)->{cpu.loadByteRegToReg(cpu.getERegister(),cpu.getHRegister());}),
	LD_E_L(0x5D,4,(cpu)->{cpu.loadByteRegToReg(cpu.getERegister(),cpu.getLRegister());}),
	
	LD_H_B(0x60,4,(cpu)->{cpu.loadByteRegToReg(cpu.getHRegister(),cpu.getBRegister());}),
	LD_H_C(0x61,4,(cpu)->{cpu.loadByteRegToReg(cpu.getHRegister(),cpu.getCRegister());}),
	LD_H_D(0x62,4,(cpu)->{cpu.loadByteRegToReg(cpu.getHRegister(),cpu.getDRegister());}),
	LD_H_E(0x63,4,(cpu)->{cpu.loadByteRegToReg(cpu.getHRegister(),cpu.getERegister());}),
	LD_H_H(0x64,4,(cpu)->{cpu.loadByteRegToReg(cpu.getHRegister(),cpu.getHRegister());}),
	LD_H_L(0x65,4,(cpu)->{cpu.loadByteRegToReg(cpu.getHRegister(),cpu.getLRegister());}),
	
	LD_L_B(0x68,4,(cpu)->{cpu.loadByteRegToReg(cpu.getLRegister(),cpu.getBRegister());}),
	LD_L_C(0x69,4,(cpu)->{cpu.loadByteRegToReg(cpu.getLRegister(),cpu.getCRegister());}),
	LD_L_D(0x6A,4,(cpu)->{cpu.loadByteRegToReg(cpu.getLRegister(),cpu.getDRegister());}),
	LD_L_E(0x6B,4,(cpu)->{cpu.loadByteRegToReg(cpu.getLRegister(),cpu.getERegister());}),
	LD_L_H(0x6C,4,(cpu)->{cpu.loadByteRegToReg(cpu.getLRegister(),cpu.getHRegister());}),
	LD_L_L(0x6D,4,(cpu)->{cpu.loadByteRegToReg(cpu.getLRegister(),cpu.getLRegister());})
	;
	
	private final int hexValue;
	private final int cycles;
	private final Z80Cpu.OpCodeCommand command;
	
	private final static HashMap<Integer,OpCode> OP_CODE_MAP = new  HashMap<Integer,OpCode>();
	
	static{
		for (OpCode opCode:OpCode.values()){
			OP_CODE_MAP.put(opCode.hexValue, opCode);
		}
	}
	
	
	private OpCode(int hexValue,int cycles,Z80Cpu.OpCodeCommand command) {
		this.hexValue = hexValue;
		this.cycles = cycles;
		this.command = command;
	}
	
	public int getHexValue(){
		return hexValue;
	}
	
	public int getCycles(){
		return this.cycles;
	}
	
	public void execute(Z80Cpu cpu){
		this.command.execute(cpu);
	}
	
	public static OpCode decode(int instruction){
		
		if (OP_CODE_MAP.containsKey(instruction)) return OP_CODE_MAP.get(instruction);
		
		throw new IllegalArgumentException("Unable to decode instruction: "+instruction);
	}
	
	
}
